package jmcnet.libcommun.communs.helpers
{
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	import jmcnet.libcommun.communs.exceptions.ExceptionTechnique;
	import jmcnet.libcommun.communs.structures.HashTable;
	import jmcnet.libcommun.logger.JMCNetLog4JLogger;
	
	import mx.utils.ObjectUtil;

	public class HelperClass
	{
		private static var _log:Boolean=false;
		private static var log:JMCNetLog4JLogger = JMCNetLog4JLogger.getLogger(getQualifiedClassName(HelperClass));
		
		public function HelperClass() {
			throw new UninitializedError("The class HelperClass is static and cannot be instanciated.");
		}
		
		/**
		 * Get all object's attributes order by name
		 *
		 * @param o The object to convert
		 * @return An HashTable containing the object attributes
		 */
		public static function getObjectAttributes( o:Object ):HashTable
		{
			if (_log) log.debug("Appel objectToString o="+ObjectUtil.toString(o));
			var result:HashTable = new HashTable();
			
			var key:String;
			var value:Object;
			
			// determine if o is a class instance or a plain object
			var classInfo:XML = describeType( o );
			if ( classInfo.@name.toString() == "Object" )
			{
				if (_log) log.debug("classInfo.@name= Object");
				// the value of o[key] in the loop below - store this 
				// as a variable so we don't have to keep looking up o[key]
				// when testing for valid values to convert
				
				// loop over the keys in the object and add their converted
				// values to the string
				for ( key in o )
				{
					// assign value to a variable for quick lookup
					value = o[key];
					
					if (_log) log.debug("Object key="+key+" value="+value);
					
					// don't add function's to the JSON string
					if ( value is Function )
					{
						// skip this key and try another
						continue;
					}
					
					result.addItem(key, value);	
				}
			}
			else {// o is a class instance
				if (_log) log.debug("classInfo.@name != Object");
				
				// Loop over all of the variables and accessors in the class and 
				// serialize them along with their values.
				for each ( var v:XML in classInfo..*.( 
					name() == "variable"
					||
					( 
						name() == "accessor"
						// Issue #116 - Make sure accessors are readable
						&& attribute( "access" ).charAt( 0 ) == "r" ) 
				) )
				{
					// Issue #110 - If [Transient] metadata exists, then we should skip
					if ( v.metadata && v.metadata.( @name == "Transient" ).length() > 0 )
					{
						continue;
					}
					
					if (_log) log.debug("nonObject key="+v.@name.toString()+" value="+o[v.@name]);
					result.addItem(v.@name.toString(), o[v.@name]);
				}				
			}
			
			if (_log) log.debug("getObjectAttributes before sorting result="+result.toString());
			
			// Sorting the HashTable
			result.sortKey(0);
			
			if (_log) log.debug("End of getObjectAttributes result="+result.toString());
			return result;
		}
		
		public static function getClassNameOfAttribute(o:Object, attrName:String):String {
			// determine if o is a class instance or a plain object
			var classInfo:XML = describeType( o );
			for each (var attribute:XML in classInfo..*.( 
					name() == "variable"
					||
					( 
						name() == "accessor"
						// Issue #116 - Make sure accessors are readable
						&& attribute( "access" ).charAt( 0 ) == "r" ) 
					)) {
				if (attribute.@name.toString() == attrName) {
					return attribute.@type;
				}
			}
			return "Object";
		}
		
		public static function getArrayElementTypeOfArray(o:Object, attrName:String):String {
			if (! o is Array ) {
				throw new ExceptionTechnique("getArrayElementTypeOfArray can only be called with an Array object.");
			}
			// determine if o is a class instance or a plain object
			var classInfo:XML = describeType( o );
			for each (var attribute:XML in classInfo..*.( 
					name() == "variable"
					||
					( 
						name() == "accessor"
						// Issue #116 - Make sure accessors are readable
						&& attribute( "access" ).charAt( 0 ) == "r" ) 
					))
			{
				if (attribute.@name.toString() == attrName) {
					log.debug("getArrayElementTypeOfArray attribute="+attribute.toString());
					return attribute..metadata.(@name=="ArrayElementType").arg.@value.toString();
				}
			}
			return "Object";
		}
		
	}
}